<?php

namespace App\Http\Controllers;

use App\Exceptions\ApiException;
use App\Exports\BookTitleExport29;
use App\Exports\BookTitleExport8;
use App\Exports\BookTitleExportLY;
use App\Imports\BookTitleImport29;
use App\Imports\BookTitleImport8;
use App\Imports\BookTitleImportLY;
use App\Jobs\BatchUpPdf;
use App\Models\PartCompany;
use App\Models\Project;
use App\Models\ProjectClassify;
use App\Models\User;
use App\Services\AttachmentService;
use App\Services\BaseService;
use App\Services\BookTitleMergeService;
use App\Services\BookTitleService;
use App\Services\CommonService;
use App\Services\ErrorMsgService;
use App\Services\ProjectClassifyService;
use App\Services\ProjectService;
use App\Services\SearchStageService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Maatwebsite\Excel\Facades\Excel;
use PHPUnit\Util\Exception;

/**
 * 案卷目录控制器类
 * Class BookTitleController
 * @package App\Http\Controllers
 */
class BookTitleController extends Controller
{
    public $bookTitleService;
    public $projectService;
    public $approvalService;

    public function __construct(BookTitleService $bookTitleService, ProjectService $projectService)
    {
        parent::__construct();
        $this->bookTitleService = $bookTitleService;
        $this->projectService   = $projectService;
    }

    /**
     * 数据列表 （案卷目录）
     *
     * 首先：建设、监理和施工都能操作自己的案卷和卷内
     * 另外：建设单位可以查看监理和施工资料，不可操作
     *      监理单位可以查看自己关联的施工资料，不可操作
     * 获取数据后，进行 redis 集合判断，如果对应的集合为空，初始化该项目分类下的档案号集合；如果不为空，忽略
     * redis集合用在excel导入数据时判断档案号是否存在
     * 为了保证 数据一致性，先清除原来的（只有具备操作权限的人触发列表），重新存入 project_classify_id 对应的档案号
     */
    public function index(Request $request)
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');
        $type              = $request->input('type');
        $searchType        = $request->input('search_type');
        $perPage           = $request->input('per_page');
        $keyword           = format_str($request->input('keyword', ''));
        $archivesNo        = format_str($request->input('archives_no'));
        $titleKeyword      = format_str($request->input('title_keyword'));
        $fileNumber        = format_str($request->input('file_number'));
        $isHaveChildren    = format_str($request->input('is_have_children')); //是否有下级
        $firstNode         = $request->input('first_node');

        if ($firstNode) { //当前参建单位下的全部数据
            $tables = CommonService::getSubTables("book_titles_{$partCompanyId}"); //获取当前参建单位下的案卷全部的分表
            $list   = (new SearchStageService())->getUnionBookTitleList($tables, $perPage, $titleKeyword, $archivesNo, $fileNumber);
        } elseif ($isHaveChildren) { //有下级
            $redisKey = "childrenBts:{$partCompanyId}:{$projectClassifyId}";
            $tables = redis()->smembers($redisKey); //获取当前参建单位下的案卷全部的分表
            $list = (new SearchStageService())->getUnionBookTitleList($tables, $perPage, $titleKeyword, $archivesNo, $fileNumber);
        } else {
            if (BookTitleService::isAllowOperate($partCompanyId)) { //导入案卷时，比对档案号是否已存在用到
                BookTitleService::clearArchivesNo($projectClassifyId); //1-用户可操作，0-不可以
            }
            $list = $this->bookTitleService->getList($partCompanyId, $projectClassifyId, $perPage, $keyword, $type, $searchType);
        }
        return $this->success($list);
    }

    /**
     * 检查案卷目录, 如果有异常案卷，只检查异常案卷, 没有异常案卷，则检查全部
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 15:48
     */
    public function check(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');

        $selectIds = $request->input('ids', '');
        $idStr     = implode(',', to_array($selectIds));
        $msg       = $this->bookTitleService->checkData($partCompanyId, $projectClassifyId, $selectIds);

        CommonService::addProjectActionLog('检查案卷', $idStr, $partCompanyId, $projectClassifyId);

        return $this->success('', $msg); //检查完毕！异常数据已被标
    }

    /**
     * 查询单个案卷目录 （卷内目录上方的案卷信息）
     *
     * @param Request $request
     * @return JsonResponse
     * @author: lxk
     * @date: 2024/3/19 10:17
     */
    public function show(Request $request): JsonResponse
    {
        $id                = $request->input('id');
        $type              = $request->input('type', 0);
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');

        $data = $this->bookTitleService->detail($type, $id, $partCompanyId, $projectClassifyId);

        return $this->success($data);
    }

    /**
     * 批量替换
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/1/13 16:36
     */
    public function batchReplace(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');
        $bookTitleIds      = to_array($request->input('book_title_ids'));
        $type              = $request->input('type'); //替换类型 1档案号，2案卷题名
        $isEffectCatalog   = $request->input('is_effect_catalog'); //是否用于卷内
        $beforeStr         = format_str($request->input('before_str')); //需替换字符
        $afterStr          = format_str($request->input('after_str')); //替换为

        $bookTitleInstance   = CommonService::getBookTitleInstance($partCompanyId, $projectClassifyId);
        $bookCatalogInstance = CommonService::getBookCatalogInstance($partCompanyId, $projectClassifyId);
        if ($type == 1 || $type == 2) {
            $this->bookTitleService->replaceTitleStr($bookTitleInstance, $bookTitleIds, $type, $beforeStr, $afterStr);
            if ($isEffectCatalog) {
                $this->bookTitleService->replaceCatalogStr($bookCatalogInstance, $bookTitleIds, 4, $beforeStr, $afterStr);
            }
        } else {
            $this->bookTitleService->replaceCatalogStr($bookCatalogInstance, $bookTitleIds, $type, $beforeStr, $afterStr);
        }
        $typeCon = $this->bookTitleService->getType($type, $isEffectCatalog);
        CommonService::addProjectActionLog("批量替换:{$typeCon}，将【{$beforeStr}】替换为【{$afterStr}】", 0, $partCompanyId, $projectClassifyId);

        return $this->success('', '替换成功！');
    }

    /**
     * 添加案卷
     *
     * @param Request $request
     * @return JsonResponse
     * @author: lxk
     * @date: 2024/3/19 10:16
     */
    public function store(Request $request): JsonResponse
    {
        $params = $request->all();
        $count  = $request->input('count', 1);
        if (!BookTitleService::checkCount($count)) {
            return $this->error('数量不合法！必须是1到100之间的整数');
        }
        if (!$this->bookTitleService->addNewData($params)) {
            return $this->error('新增失败~');
        }
        $msg = request()->filled('id') ? "成功插入【{$count}】条案卷" : "成功新增【{$count}】条案卷";

        return $this->success('', $msg);
    }

    /**
     * 修改案卷
     *
     * @param Request $request
     * @return JsonResponse
     * @author: lxk
     * @date: 2024/3/19 9:50
     */
    public function update(Request $request): JsonResponse
    {
        $id = $request->input('id');
        if (!BookTitleService::isAllowOperate($request->input('part_company_id'))) {
            return $this->error('无权修改！');
        }
        if (!$this->bookTitleService->updateData($id, $request->all())) {
            return $this->error('修改失败~');
        }

        return $this->success('', '修改成功！');
    }

    /**
     * 批量导入PDF
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 9:51
     */
    public function batchImportPdfInTitle(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');
        $file              = $request->file('file');
        $storePath         = $file->store('public/pdfs/uploadPdfs'); //自定义上传文件的临时地址
        $tmpFile           = str_replace('\\', '/', storage_path('app/' . $storePath)); //系统默认的临时文件地址会自动清除文件，太快，队列来不及使用就被清除

        if (!$file->isValid()) throw new Exception('文件不合法');

        $originalName = $file->getClientOriginalName(); //获取原文件名
        $ext          = $file->getClientOriginalExtension(); //获取文件后缀名
        $folder       = AttachmentService::getFolder($ext);
        $md5FileName  = md5($originalName . time()) . '.' . $ext; //md5加密处理
        $projectId    = PartCompany::query()->where('id', $partCompanyId)->value('project_id');
        $pathName     = "{$folder}/pro{$projectId}/part{$partCompanyId}/unitPro{$projectClassifyId}/$md5FileName"; //存储在OSS的文件路径

        $data['file_size']    = round($file->getSize() / 1024); //把byte转化为KB
        $data['origin_name']  = substr($originalName, 0, strripos($originalName, '.')); //去掉原文件名后缀.pdf
        $data['path_name']    = $pathName;
        $data['tmp_file']     = $tmpFile;
        $data['user_id']      = user_id();
        $data['user_name']    = user() ? user()->nickname : User::query()->where('id', user_id())->value('nickname');
        $data['company_id']   = company_id();
        $data['company_name'] = company_name();
        $data['role_name']    = role_name();

        $bookTitleId = $this->bookTitleService->checkPdfFileName($partCompanyId, $projectClassifyId, $originalName); //判断PDF文件名是否有对应的档号
        BatchUpPdf::dispatch($partCompanyId, $projectClassifyId, $bookTitleId, $data); //把文件上传放到队列里面执行

        return $this->success('', '上传成功！');
    }

    /**
     * 问题标记
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 9:51
     */
    public function problemMark(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');

        $bookTitleInstance = (new BaseService())->getBookTitleInstance($partCompanyId, $projectClassifyId);

        $ids     = to_array($request->input('ids'));
        $problem = format_str($request->input('problem'));
        if (!BookTitleService::problemMark($bookTitleInstance, $ids, $problem)) {
            return $this->error('标记失败！');
        }
        $ids = implode(',', $ids);
        CommonService::addProjectActionLog("案卷问题标记:$problem", $ids, $partCompanyId, $projectClassifyId);

        return $this->success('', '标记成功！');
    }

    /**
     * 问题已解决
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 9:52
     */
    public function problemResolved(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');
        $bookTitleId       = $request->input('id', 0);
        $bookTitleIds      = $request->input('ids', '');

        $bookTitleInstance = (new BaseService())->getBookTitleInstance($partCompanyId, $projectClassifyId);
        if ($bookTitleIds) {
            $problem = '批量解除';
            $bookTitleInstance->whereIn('id', to_array($bookTitleIds))->update(['problem' => '']);
        } else {
            $problem = $bookTitleInstance->where('id', $bookTitleId)->value('problem');
            $bookTitleInstance->where('id', $bookTitleId)->update(['problem' => '']);
        }
        CommonService::addProjectActionLog("案卷问题已解决:$problem", $bookTitleId, $partCompanyId, $projectClassifyId);

        return $this->success('', '操作成功！');
    }

    /**
     * 柜号标记提示
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 9:53
     */
    public function cabinMarkNotice(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id'); //参建单位
        $projectClassifyId = $request->input('project_classify_id'); //项目分类目录
        $ids               = $request->input('ids', 0); //如果为0，视为全选
        $bookTitleInstance = (new BaseService())->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $mergeInfo         = BookTitleMergeService::isHaveMerge($projectClassifyId);
        if (empty($ids)) {
            $serialNos    = $bookTitleInstance
                ->where('part_company_id', $partCompanyId)
                ->orderBy('serial_no')
                ->pluck('serial_no')
                ->toArray();
            $min          = min($serialNos);
            $max          = max($serialNos);
            $returnNotice = "您未指定数据，将标记序号为【{$min}~{$max}】的全部数据";
            if (!empty($mergeInfo)) {
                $returnNotice = "您未指定数据，将标记序号为【{$min}~{$max}】之间的案卷";
            }
        } else {
            $count        = count(to_array($ids));
            $returnNotice = "将标记勾选的【{$count}】条案卷数据";
        }
        $data = [
            'notice' => $returnNotice
        ];

        return $this->success($data, '操作成功！');
    }

    /**
     * 标记柜号
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 10:06
     */
    public function cabinMark(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id'); //参建单位
        $projectClassifyId = $request->input('project_classify_id'); //项目分类目录
        $cabinNo           = $request->input('cabin_no'); //柜号
        $ids               = $request->input('ids', 0); //如果为0，视为全选
        $bookTitleInstance = (new BaseService())->getBookTitleInstance($partCompanyId, $projectClassifyId);
        $res               = $bookTitleInstance
            ->when($ids, function ($query) use ($ids, $cabinNo) {
                $query->whereIn('id', to_array($ids));
            })->update(['cabin_no' => $cabinNo]);

        if (!$res) {
            return $this->error('操作失败！');
        }
        CommonService::addProjectActionLog("标记柜号:{$cabinNo}", $ids, $partCompanyId, $projectClassifyId);

        return $this->success('', '操作成功！');
    }

    /**
     * 标记柜号
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 10:06
     */
    public function blueprintMark(Request $request): JsonResponse
    {
        $partCompanyId       = $request->input('part_company_id'); //参建单位
        $projectClassifyId   = $request->input('project_classify_id'); //项目分类目录
        $bookTitleIds        = $request->input('ids', 0); //如果为0，视为全选
        $bookCatalogInstance = (new BaseService())->getBookCatalogInstance($partCompanyId, $projectClassifyId);
        $res                 = $bookCatalogInstance->whereIn('book_title_id', to_array($bookTitleIds))->update(['is_pic' => 1]);
        if (!$res) {
            return $this->error('操作失败！');
        }
        CommonService::addProjectActionLog("标记蓝图", $bookTitleIds, $partCompanyId, $projectClassifyId);

        return $this->success('', '操作成功！');
    }

    /**
     * 导入数据 注意！！！文件后缀名 xls 或者 xlsx 应该创建文件的时候指定，通过重命名修改文件后缀的的会导入失败
     *
     * @param Request $request
     * @return JsonResponse
     * @throws ApiException
     * @author: lxk
     * @date: 2024/3/19 10:06
     */
    public function import(Request $request): JsonResponse
    {
        $partCompanyId   = $request->input('part_company_id'); //参建单位
        $combinationId   = $this->projectService->getDossierCombination($partCompanyId);
        $redisKey        = ProjectClassifyService::getProjectClassifyKey($partCompanyId); //存放项目分类树形列表，导入案卷进行数据比对
        $importDetailKey = get_user_key('import_bt_detail'); //记录单位工程导入了几条数据
        if (!redis()->get($redisKey)) throw new ApiException('请刷新页面~'); //目的是让用户请求下 【获取项目分类树】接口
        try {
            $this->bookTitleService->isFileValid($request); //验证文件是否合法
            switch ($combinationId) { //判断导入案卷数据时，使用哪种格式
                case 2:  //8号文件
                    Excel::import(new BookTitleImport8($partCompanyId, $importDetailKey), $request->file('file'));
                    break;
                case 3:  //兰渝格式
                    Excel::import(new BookTitleImportLY($partCompanyId, $importDetailKey), $request->file('file'));
                    break;
                default: //29号文件
                    Excel::import(new BookTitleImport29($partCompanyId, $importDetailKey), $request->file('file'));
            }
            $projectClassifyCount = $bootTitleCount = 0;
            if ($detailList = redis()->hgetall($importDetailKey)) { //如果有数据导入，格式化一下导入详情并返回展示
                foreach ($detailList as $key => $val) { //$key 是档案号，$val是该档案号的单位工程导入的卷内数量
                    $projectClassifyId = ProjectClassify::query()->where('title', $key)->value('id');
                    CommonService::addProjectActionLog("导入案卷数据{$val}条【{$key}】", 0, $partCompanyId, $projectClassifyId);
                    $bootTitleCount       += $val;
                    $projectClassifyCount += 1;
                }
            }
            $canNotImportArchiveNos = redis()->sdiff($importDetailKey . ":AllImportArchiveNos", $importDetailKey . ":CanImportArchiveNos");
            redis()->del($importDetailKey); //redis用完删除
            redis()->del($importDetailKey . ":AllImportArchiveNos");
            redis()->del($importDetailKey . ":CanImportArchiveNos");
            if (!$bootTitleCount && !$projectClassifyCount) {
                return $this->error('没找到对应的单位工程档号，数据导入0！案卷档案号除去序号部分，剩余应和单位工程档案号一致');
            } else {
                CommonService::isNeedUpdatePaperCount($partCompanyId, 1);//告诉参建单位需要更新下统计
                if (count($canNotImportArchiveNos)) {
                    $canNotImportArchiveNos = implode('，', $canNotImportArchiveNos);
                    return $this->success('', "共导入【{$bootTitleCount}】条数据，涉及【{$projectClassifyCount}】个单位工程；然而没有找到 【{$canNotImportArchiveNos}】这些单位工程，请先创建！");
                } else {
                    return $this->success('', "共导入【{$bootTitleCount}】条数据，涉及【{$projectClassifyCount}】个单位工程；请核对每列数据是否正确！");
                }
            }
        } catch (Exception $exception) {
            ErrorMsgService::write($request, $request->url());
            return $this->error($exception->getMessage());
        }
    }

    /**
     * 导出项目全部案卷
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     * @author: lxk
     * @date: 2023/10/26 15:47
     */
    public function exportByProject(Request $request): JsonResponse
    {
        $projectId = $request->input('project_id');

        $downloadUrl = $this->bookTitleService->exportByProject($projectId);

        return $this->success($downloadUrl);
    }

    /**
     * 档案号后缀需要补充几个前导 0，例如  有的档案号 1，有的 001， 有的 0001 $zeroCount 为 2，表示需要把 1,变为 001这种格式
     *
     * @param Request $request
     * @return JsonResponse
     * @author: lxk
     * @date: 2024/3/19 10:10
     */
    public function zeroFill(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');
        $zeroCount         = $request->input('count');

        if (!in_array($zeroCount, [0, 1, 2, 3])) {
            return $this->error('数量不合法！只能是 0,1,2,3其中一个');
        }
        $this->bookTitleService->zeroFill($partCompanyId, $projectClassifyId, $zeroCount); //返回补齐的案卷数量

        CommonService::addProjectActionLog('档案号后缀需要补充几个前导 0', 0, $partCompanyId, $projectClassifyId);

        return $this->success('', "操作成功！");
    }

    /**
     * 清空案卷
     *
     * @param Request $request
     * @return JsonResponse
     * @author: lxk
     * @date: 2024/1/26 16:36
     */
    public function clearData(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');

        $this->bookTitleService->clearData($partCompanyId, $projectClassifyId);

        CommonService::addProjectActionLog('清空案卷', 0, $partCompanyId);
        CommonService::isNeedUpdatePaperCount($partCompanyId, 1); //告诉参建单位需要更新下统计
        //注意合并案卷的情况......
        return $this->success('', '已清空当前标段数据~');
    }

    /**
     * 导出案卷时给出最小和最大范围
     */
    public function getMinAndMaxSerial(Request $request): JsonResponse
    {
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');
        $bookTitleInstance = CommonService::getBookTitleInstance($partCompanyId, $projectClassifyId);
        $info = $bookTitleInstance->where('id', '>', 0)->first(); //随便找个案卷就行
        if (empty($info)) {
            return $this->success([
                'min_serial_no' => 0,
                'max_serial_no' => 0,
            ]);
        }
        $archivesNoInfo = BookTitleService::getSerialNos($bookTitleInstance, $info->archives_no);

        return $this->success([
            'min_serial_no' => to_int($archivesNoInfo['min']),
            'max_serial_no' => to_int($archivesNoInfo['max']),
        ]);
    }


    /**
     * 回收站列表 （进行软删除的数据）
     */
    public function recycle(Request $request)
    {
        $data = $this->bookTitleService->getRecycles($request);

        return $this->success($data);
    }

    /**
     * 单个或批量都行
     * 软删除，ids可以是以","逗号分割的字符串，也可以是数组形式
     * 删除若干数据后，剩余的数据序号和档案号重新排列，第一个序号为1 从小到大递增
     * @author: lxk
     * @date: 2023/5/19 17:56
     */
    public function destroyMore(Request $request)
    {
        $bookTitleIds      = to_array($request->input('ids'));
        $partCompanyId     = $request->input('part_company_id');
        $projectClassifyId = $request->input('project_classify_id');
        if (empty($bookTitleIds)) {
            return $this->error('', '未指定数据！无法删除');
        }
        if (!$this->bookTitleService->delData($partCompanyId, $projectClassifyId, $bookTitleIds)) {
            return $this->error('', '删除失败');
        }
        CommonService::isNeedUpdatePaperCount($partCompanyId, 1); //告诉参建单位需要更新下统计

        return $this->success('', '删除成功');
    }

}
